package by.demchenko.sample.dataaccess.impl;

import by.demchenko.sample.dataaccess.base.CourseDao;
import by.demchenko.sample.datamodel.*;
import by.demchenko.sample.datamodel.enums.ScheduleStatus;
import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import javax.persistence.metamodel.SingularAttribute;
import java.util.List;

@Repository
public class CourseDaoImpl extends AbstractDaoImpl<Integer, Course> implements CourseDao {

    protected CourseDaoImpl() {
        super(Course.class);
    }

    @Override
    public Integer getCount() {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

        CriteriaQuery<Long> criteria = cBuilder.createQuery(Long.class);
        Root<Course> root = criteria.from(Course.class);

        criteria.select(cBuilder.count(root));

        TypedQuery<Long> query = getEm().createQuery(criteria);
        return query.getSingleResult().intValue();
    }

//    @Override
//    public List<Course> getCourseToRegister() {
//        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
//        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
//        Root<Course> root = q.from(Course.class);
//
//        root.fetch(Course_.teacher);
//        root.fetch(Course_.schedules);
//        q.distinct(true);
//
//        q.select(root);
//        q.orderBy(new OrderImpl(root.get(Course_.schedules), true));
//
//        TypedQuery<Course> query = getEm().createQuery(q);
//
//        List<Course> results = query.getResultList();
//        return results;
//    }

    @Override
    public List<Schedule> getCourseToRegister() {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Schedule> q = cBuilder.createQuery(Schedule.class);
        Root<Schedule> root = q.from(Schedule.class);

        root.fetch(Schedule_.course);
        q.distinct(true);

        q.select(root);
        q.orderBy(new OrderImpl(root.get(Schedule_.startDate), true));

        Predicate p1 = cBuilder.equal(root.get(Schedule_.status), ScheduleStatus.OPENED);

//        Expression<Integer> pTotal = root.get("total");
//        Expression<Integer> pRegistered = root.get("registered");
//        Expression<Integer> sum1 = cBuilder.diff(pTotal, pRegistered);
//
//        Predicate p2 = cBuilder.greaterThan(sum1, 0);

        q.where(p1);

        //q.where(p1);

        TypedQuery<Schedule> query = getEm().createQuery(q);

        List<Schedule> results = query.getResultList();
        return results;
    }

    @Override
    public List<Course> getCourseToRegister(Section section) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        root.fetch(Course_.teacher);
        root.fetch(Course_.schedules);
        q.distinct(true);

        q.select(root);
        Predicate p1 = cBuilder.equal(root.get(Course_.section), section);
        q.where(p1);

        TypedQuery<Course> query = getEm().createQuery(q);

        List<Course> results = query.getResultList();
        return results;
    }

    @Override
    public List<Course> getCourseForTable(Person teacher) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        root.fetch(Course_.teacher);
        root.fetch(Course_.schedules);
        root.fetch(Course_.section);
        q.distinct(true);

        q.select(root);
        Predicate p1 = cBuilder.equal(root.get(Course_.teacher), teacher);
        q.where(p1);

        TypedQuery<Course> query = getEm().createQuery(q);

        List<Course> results = query.getResultList();
        return results;
    }

    @Override
    public List<Course> getCourseRegistrationStatus(ScheduleStatus status) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        Join schedules = root.join("schedules");

        root.fetch(Course_.schedules);
        q.distinct(true);

        q.select(root);
        Predicate p1 = cBuilder.equal(schedules.get(Schedule_.status), status);
        q.where(p1);

        TypedQuery<Course> query = getEm().createQuery(q);

        List<Course> results = query.getResultList();
        return results;
    }

    public List<Course> findAll(boolean fetchJoins) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        root.fetch(Course_.teacher);
        root.fetch(Course_.section);
        q.distinct(true);

        q.select(root);
        q.orderBy(new OrderImpl(root.get(Course_.name), true));

        TypedQuery<Course> query = getEm().createQuery(q);

        List<Course> results = query.getResultList();
        return results;
    }

    public List<Course> findAll(SingularAttribute<Course, ?> attr, boolean ascending, int startRecord, int pageSize, boolean fetchJoins) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        if (fetchJoins) {
            root.fetch(Course_.teacher);
            root.fetch(Course_.section);
        }
        q.distinct(true);

        q.select(root);
        q.orderBy(new OrderImpl(root.get(attr), ascending));

        TypedQuery<Course> query = getEm().createQuery(q);
        query.setFirstResult(startRecord);
        query.setMaxResults(pageSize);

        List<Course> results = query.getResultList();
        return results;
    }

    @Override
    public List<Course> getCourseBySection(Section section) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        root.fetch(Course_.teacher);
        root.fetch(Course_.section);
        q.distinct(true);

        q.select(root);
        Predicate p1 = cBuilder.equal(root.get(Course_.section), section);
        q.where(p1);

        TypedQuery<Course> query = getEm().createQuery(q);

        List<Course> results = query.getResultList();
        return results;
    }

    @Override
    public Course findByName(String name) {
        CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
        CriteriaQuery<Course> q = cBuilder.createQuery(Course.class);
        Root<Course> root = q.from(Course.class);

        q.select(root);
        Predicate p1 = cBuilder.equal(root.get(Course_.name), name);
        q.where(p1);

        TypedQuery<Course> query = getEm().createQuery(q);

        List<Course> results = query.getResultList();
        if (results.size() != 1) {
            return null;
        } else {
            return results.get(0);
        }
    }
}